Skip to content

The half second of map that should not have been there - #226

Merged
0xward merged 1 commit into
mainfrom
claude/nullstate-onchain-text-q6g2a3
Aug 2, 2026
Merged

The half second of map that should not have been there#226
0xward merged 1 commit into
mainfrom
claude/nullstate-onchain-text-q6g2a3

Conversation

@0xward

@0xward 0xward commented Aug 2, 2026

Copy link
Copy Markdown
Owner

Outside MiniPay the world map was visible under the gate for about half a second. No amount of React could fix it: the map is in the prerendered HTML, so it paints before a single line of JavaScript has run. That is deliberate and worth keeping — it is what /game's LCP optimisation buys, and MiniPay players (who never see the gate) keep the whole benefit.

So the fix is the only thing that can run earlier than React: an inline script in the document. When the gate is certain to appear it marks <html> before the first paint and CSS hides the home screen. When it is not, it does nothing at all and the map paints exactly as it does today.

Why an attribute and not an overlay

The overlay version was written first, and measured. Next's hydration reconciles the children of <body> and deleted the node at 261 ms — roughly 600 ms before the gate was ready to cover anything, so the map was on screen from 323 ms to 882 ms anyway. An attribute React never rendered is not part of that reconciliation and survives.

Hiding by visibility rather than removing, so the map's image request stays intact: a player who skips the gate gets the map immediately instead of paying for it twice.

Measured, polling every 30 ms for 5 s

mark home screen gate
inside MiniPay none visible 102 ms
new player, outside 144→509 ms never visible 547 ms
returning (skipped once) none visible 95 ms

MiniPay is untouched. A returning player pays no black screen for a gate they will never be shown.

The cost, stated plainly

This is a third copy of the MiniPay rule, and it was accepted knowingly. Every branch in the script can only decide "do not veil", so a drift between the copies costs a non-MiniPay player half a second of map and can never reach a MiniPay player at all.

The 3 s dead-man timer matters as much as the conditions: if React never arrives — a chunk 404, a JS error — the mark lifts by itself rather than leaving somebody on a black screen.

Testing

✓ 41 assertions in test:privy — including one that runs in CI on the default
  path, asserting the veil never fires when the flag is off
✓ test:streak-ui  ✓ test:season-ui  ✓ test:pass-cards
✓ tsc clean  ✓ build 142/242 kB (unchanged)
✓ check:copy  ✓ check:cssvars  ✓ check:market  ✓ audit  ✓ lint 35

Generated by Claude Code

OWNER: *"terus kejar yg 0.5 detik itu."*

Outside MiniPay the world map was visible under the gate for about half a
second, and no amount of React could fix it: the map is in the PRERENDERED
HTML, so it paints before a single line of JavaScript has run. That is
deliberate and worth keeping — it is what /game's LCP optimisation buys, and
MiniPay players, who never see the gate, keep the whole benefit.

So the fix is the only thing that can run earlier than React: an inline script
in the document. When the gate is certain to appear it marks <html> before the
first paint and CSS hides the home screen; when it is not, it does nothing at
all and the map paints exactly as it does today.

It marks <html> rather than appending an overlay, and that was measured, not
assumed. The overlay version was written first and Next's hydration — which
reconciles the children of <body> — deleted the node at 261ms, roughly 600ms
before the gate was ready to cover anything. An attribute React never rendered
is not part of that reconciliation and survives.

Hiding by `visibility` rather than removing: the map's image request stays
intact, so a player who skips the gate gets the map immediately instead of
paying for it twice.

Measured after the change, polling every 30ms for 5s:

    inside MiniPay             no mark, home visible at 102ms   (untouched)
    new player outside         mark 144→509ms, home NEVER visible, gate 547ms
    returning (skipped once)   no mark, home visible at 95ms    (no black wait)

This is a THIRD copy of the MiniPay rule, and the cost was accepted knowingly:
every branch in the script can only decide "do not veil", so a drift between
the copies costs a non-MiniPay player half a second of map and can never reach
a MiniPay player at all. The 3s dead-man timer matters as much — if React never
arrives, the mark lifts by itself rather than leaving somebody on a black
screen.

Verified: tsc clean, build unchanged at 142/242 kB, 41 assertions in test:privy
including one that runs in CI on the default path, plus test:streak-ui,
test:season-ui, test:pass-cards, check:copy, check:cssvars, check:market and
audit. Lint 35.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017A764RdnwpyWnG7uCNhMiQ
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@vercel

vercel Bot commented Aug 2, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
nullstate Ready Ready Preview Aug 2, 2026 5:10pm

@0xward
0xward merged commit 82f06ab into main Aug 2, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant